#include "mainwindow.h"
#include "ui_mainwindow.h"


#include <QHBoxLayout>
#include <QIcon>
#include <QListWidgetItem>


MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow),
    m_cylinder_count(0),
    m_sphere_count(0),
    m_cube_count(0),
    m_cone_count(0)
{
    ui->setupUi(this);

    m_gl_widget = new GLWidget();
    ui->canvas_frame->setLayout(new QHBoxLayout);
    ui->canvas_frame->layout()->addWidget(m_gl_widget);

    ui->csg_treeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
    connect_slots();
    setup_canvas();
}

MainWindow::~MainWindow()
{
    delete m_gl_widget;
    delete ui;
}


void MainWindow::connect_slots(){
    connect(ui->csg_treeWidget,SIGNAL(customContextMenuRequested(QPoint)),this,SLOT(context_menu(QPoint)));
    connect(ui->dif_red_horizontalSlider,SIGNAL(valueChanged(int)),this,SLOT(update_colors()));
    connect(ui->dif_green_horizontalSlider,SIGNAL(valueChanged(int)),this,SLOT(update_colors()));
    connect(ui->dif_blue_horizontalSlider,SIGNAL(valueChanged(int)),this,SLOT(update_colors()));

    connect(ui->spec_red_horizontalSlider,SIGNAL(valueChanged(int)),this,SLOT(update_colors()));
    connect(ui->spec_green_horizontalSlider,SIGNAL(valueChanged(int)),this,SLOT(update_colors()));
    connect(ui->spec_blue_horizontalSlider,SIGNAL(valueChanged(int)),this,SLOT(update_colors()));

    connect(ui->amb_red_horizontalSlider,SIGNAL(valueChanged(int)),this,SLOT(update_colors()));
    connect(ui->amb_green_horizontalSlider,SIGNAL(valueChanged(int)),this,SLOT(update_colors()));
    connect(ui->amb_blue_horizontalSlider,SIGNAL(valueChanged(int)),this,SLOT(update_colors()));

    connect(ui->shininess_spinBox,SIGNAL(valueChanged(int)),this,SLOT(update_colors()));

    connect(ui->cylinder_pushButton,SIGNAL(clicked()),this,SLOT(add_primitive()));
    connect(ui->cube_pushButton,SIGNAL(clicked()),this,SLOT(add_primitive()));
    connect(ui->sphere_pushButton,SIGNAL(clicked()),this,SLOT(add_primitive()));
    connect(ui->cone_pushButton,SIGNAL(clicked()),this,SLOT(add_primitive()));

    connect(ui->translate_pushButton,SIGNAL(clicked()),this,SLOT(add_transformation()));
    connect(ui->rotate_pushButton,SIGNAL(clicked()),this,SLOT(add_transformation()));
    connect(ui->scale_pushButton,SIGNAL(clicked()),this,SLOT(add_transformation()));

    connect(ui->union_pushButton,SIGNAL(clicked()),this,SLOT(add_operation()));
    connect(ui->difference_pushButton,SIGNAL(clicked()),this,SLOT(add_operation()));
    connect(ui->intersection_pushButton,SIGNAL(clicked()),this,SLOT(add_operation()));

    connect(ui->csg_treeWidget,SIGNAL(itemSelectionChanged()),this,SLOT(update_selection()));

    ui->transformations_frame->hide();
    ui->boolean_frame->hide();
    ui->materials_frame->hide();
}

void MainWindow::setup_canvas()
{

    m_canvas = new Canvas(&m_nodes_list);
    m_canvas->update_image();
    m_gl_widget->set_canvas(m_canvas);
    update_canvas();
}

void MainWindow::update_colors()
{
    if(m_current_node != NULL){
        if(m_current_node->m_node_type == PRIMITIVE_NODE){
            m_current_primitive = m_current_node.dynamicCast<CSGPrimitive>();
            glm::vec3 diffuse;
            diffuse.x = ui->dif_red_horizontalSlider->value();
            diffuse.y = ui->dif_green_horizontalSlider->value();
            diffuse.z = ui->dif_blue_horizontalSlider->value();

            glm::vec3 specular;
            specular.x = ui->spec_red_horizontalSlider->value();
            specular.y = ui->spec_green_horizontalSlider->value();
            specular.z = ui->spec_blue_horizontalSlider->value();

            glm::vec3 ambient;
            ambient.x = ui->amb_red_horizontalSlider->value();
            ambient.y = ui->amb_green_horizontalSlider->value();
            ambient.z = ui->amb_blue_horizontalSlider->value();


            m_current_primitive.data()->m_material->set_diffuse(glm::normalize(diffuse));
            m_current_primitive.data()->m_material->set_specular(glm::normalize(specular));
            m_current_primitive.data()->m_material->set_ambient(glm::normalize(ambient));
            m_current_primitive.data()->m_material->set_shininess(ui->shininess_spinBox->value());
            update_canvas();
        }
    }
}

void MainWindow::update_canvas()
{
    m_canvas->update_image();
    m_gl_widget->update_texture();
}

void MainWindow::update_selection()
{    
    ui->boolean_frame->hide();
    ui->materials_frame->hide();
    ui->transformations_frame->hide();

    QList<QTreeWidgetItem*> selected_items = ui->csg_treeWidget->selectedItems();
    if(selected_items.size() == 1){
        QTreeWidgetItem* item = selected_items.front();
        m_current_node = m_tree_nodes_map[item];
        if(m_current_node == NULL){
            return;
        }
        if(m_current_node->m_node_type == PRIMITIVE_NODE){
            ui->materials_frame->show();
        }
        if(item->parent() == NULL){
            ui->transformations_frame->show();
        }
    }
    if(selected_items.size() == 2){
        if((selected_items[0]->parent() == NULL) && (selected_items[1]->parent() == NULL)){
            ui->boolean_frame->show();
        }
    }
}

void MainWindow::add_primitive()
{
    QTreeWidgetItem* new_node;
    QPushButton *sender_button = dynamic_cast<QPushButton*>(QObject::sender());
    QString primitive_type = sender_button->text();
    QString item_name;
    item_name.append(primitive_type);
    item_name.append(" ");
    PrimitiveType type = INVALID_PRIMITIVE;
    unsigned int count = 0;
    if(primitive_type == "Cylinder"){
        m_cylinder_count++;
        count = m_cylinder_count;
        type = CYLINDER;
    }
    if(primitive_type == "Cube"){
        m_cube_count++;
        count = m_cube_count;
        type = CUBE;
    }
    if(primitive_type == "Sphere"){
        m_sphere_count++;
        count = m_sphere_count;
        type = SPHERE;
    }
    if(primitive_type == "Cone"){
        m_cone_count++;
        count = m_cone_count;
        type = CONE;
    }
    if(type != INVALID_PRIMITIVE){
        QSharedPointer<CSGNode> new_primitive = QSharedPointer<CSGNode>(new CSGPrimitive(type, PRIMITIVE_NODE, m_primitives_list.size()));
        m_current_node = new_primitive;
        m_current_primitive = new_primitive.dynamicCast<CSGPrimitive>();
        m_primitives_list.push_back(m_current_primitive);

        item_name.append(QString::number((count)));
        new_node = new QTreeWidgetItem(QStringList(item_name),m_nodes_list.size());
        m_tree_nodes_map[new_node] = m_current_node;

        ui->csg_treeWidget->addTopLevelItem(new_node);
        m_nodes_list.push_back(new_primitive);
        update_canvas();
        m_gl_widget->update_texture();
    }
}

void MainWindow::add_operation()
{
    QTreeWidgetItem* new_item;
    OperationType operation;
    QPushButton *button = dynamic_cast<QPushButton*>(QObject::sender());
    QString selected_operation = button->text();
    QString item_text;


    QList<QTreeWidgetItem*> selected_items = ui->csg_treeWidget->selectedItems();
    if(selected_items.size() == 2){
        if(selected_operation == "Union"){
            operation = UNION;
            item_text.append("Union");
        }
        if(selected_operation == "Intersection"){
            operation = INTERSECTION;
            item_text.append("Intersection");
        }
        if(selected_operation == "Difference"){
            operation = DIFFERENCE;
            item_text.append("Difference");
        }

        QSharedPointer<CSGBooleanOperation> operation_node = QSharedPointer<CSGBooleanOperation>(new CSGBooleanOperation(operation));

        int first_index = ui->csg_treeWidget->indexOfTopLevelItem(selected_items[0]);
        int second_index = ui->csg_treeWidget->indexOfTopLevelItem(selected_items[1]);

        QSharedPointer<CSGNode> node1 = QSharedPointer<CSGNode>(m_nodes_list[first_index]);
        QSharedPointer<CSGNode> node2 = QSharedPointer<CSGNode>(m_nodes_list[second_index]);

        operation_node->add_son(node1);
        operation_node->add_son(node2);
        operation_node->update_matrix();


        new_item = new QTreeWidgetItem(QStringList(item_text),m_nodes_list.size());

        QTreeWidgetItem *first_item = ui->csg_treeWidget->takeTopLevelItem(first_index);
        if(second_index > first_index){
            second_index--;
        }
        QTreeWidgetItem *second_item = ui->csg_treeWidget->takeTopLevelItem(second_index);

        if(first_item != 0){
            new_item->addChild( first_item );
        }
        if(second_item != 0){
            new_item->addChild( second_item );
        }
        ui->csg_treeWidget->addTopLevelItem(new_item);



        m_current_node = operation_node;
        m_tree_nodes_map[new_item] = m_current_node;

        m_nodes_list.removeAt(first_index);
        m_nodes_list.removeAt(second_index);
        m_nodes_list.push_back(operation_node);
        //ui->csg_treeWidget->expandAll();
        update_canvas();
    }
}

void MainWindow::add_transformation()
{
    QTreeWidgetItem* new_node;
    OperationType transformation;
    QPushButton *button = dynamic_cast<QPushButton*>(QObject::sender());
    QString selected_transformation = button->text();
    QString item_text;
    if(selected_transformation == "Translate"){
        transformation = TRANSLATION;
    }
    if(selected_transformation == "Rotate"){
        transformation = ROTATION;
    }
    if(selected_transformation == "Scale"){
        transformation = SCALE;
    }

    int index = ui->csg_treeWidget->currentIndex().row();
    if(index >= 0 ){
        GeometricTransformationsDialog *dialog = new GeometricTransformationsDialog(transformation);
        dialog->exec();
        if(dialog->result() == 1){
            glm::vec3 *transformation_vector = new glm::vec3(dialog->get_vector());
            float angle = dialog->get_angle();
            QSharedPointer<CSGNode> prim_node = (m_nodes_list[index]);
            QSharedPointer<CSGNode> node = QSharedPointer<CSGNode>(prim_node);

            QSharedPointer<CSGGeometricOperation> transform_node = QSharedPointer<CSGGeometricOperation>(new CSGGeometricOperation(transformation));
            transform_node->add_son(node);
            switch(transformation){
            case TRANSLATION:
                item_text.append("Translation");
                item_text.append("\nVector (");
                item_text.append(QString::number(transformation_vector->x));
                item_text.append(",");
                item_text.append(QString::number(transformation_vector->y));
                item_text.append(",");
                item_text.append(QString::number(transformation_vector->z));
                item_text.append(");");
                transform_node->perform_translation(*transformation_vector);
                break;
            case ROTATION:
                item_text.append("Rotation");
                item_text.append("\nVector (");
                item_text.append(QString::number(transformation_vector->x));
                item_text.append(",");
                item_text.append(QString::number(transformation_vector->y));
                item_text.append(",");
                item_text.append(QString::number(transformation_vector->z));
                item_text.append(");");
                item_text.append("\nAngle (");
                item_text.append(QString::number(angle));
                item_text.append(");");
                transform_node->perform_rotation(*transformation_vector,angle);
                break;
            case SCALE:
                item_text.append("Scale");
                item_text.append("\nVector (");
                item_text.append(QString::number(transformation_vector->x));
                item_text.append(",");
                item_text.append(QString::number(transformation_vector->y));
                item_text.append(",");
                item_text.append(QString::number(transformation_vector->z));
                item_text.append(");");
                transform_node->perform_scale(*transformation_vector);
                break;
            default:
                break;
            }
            transform_node->update_matrix();
            new_node = new QTreeWidgetItem(QStringList(item_text),m_nodes_list.size());
            ui->csg_treeWidget->addTopLevelItem(new_node);
            QTreeWidgetItem *current_item = ui->csg_treeWidget->takeTopLevelItem(index);
            new_node->addChild(current_item);

            m_current_node = transform_node;
            m_tree_nodes_map[new_node] = m_current_node;
            m_nodes_list.removeAt(index);
            m_nodes_list.push_back(transform_node);
            //ui->csg_treeWidget->expandAll();
            update_canvas();
        }
        delete dialog;
    }
}

void MainWindow::keyPressEvent(QKeyEvent *event)
{
    this->setFocus();
    glm::vec3 move_dir = glm::vec3(0.0,0.0,0.0);
    glm::vec3 rotate_vec = glm::vec3(0.0,0.0,0.0);
    float angle = 5;
    if(event->key() == Qt::Key_A){
        move_dir += glm::vec3(-1.0,0.0,0.0);
    }
    if(event->key() == Qt::Key_D){
        move_dir += glm::vec3(1.0,0.0,0.0);
    }
    if(event->key() == Qt::Key_W){
        move_dir += glm::vec3(0.0,0.0,-1.0);
    }
    if(event->key() == Qt::Key_S){
        move_dir += glm::vec3(0.0,0.0,1.0);
    }
    if(event->key() == Qt::Key_E){
        move_dir += glm::vec3(0.0,1.0,0.0);
    }
    if(event->key() == Qt::Key_Q){
        move_dir += glm::vec3(0.0,-1.0,0.0);
    }
    if(event->key() == Qt::Key_Left){
        rotate_vec = glm::vec3(0.0,1.0,0.0);
        angle = 5;
    }
    if(event->key() == Qt::Key_Right){
        rotate_vec = glm::vec3(0.0,1.0,0.0);
        angle = -5;
    }
    if(event->key() == Qt::Key_Up){
        rotate_vec = glm::vec3(1.0,0.0,0.0);
        angle = 5;
    }
    if(event->key() == Qt::Key_Down){
        rotate_vec = glm::vec3(1.0,0.0,0.0);
        angle = -5;
    }
    if(event->key() == Qt::Key_Delete){
        remove_node();
        return;
    }
    m_canvas->move_observer(move_dir);
    m_canvas->rotate_view(rotate_vec,angle);
    update_canvas();
}

void MainWindow::remove_node()
{
    QList<QTreeWidgetItem*> selected_items = ui->csg_treeWidget->selectedItems();
    QTreeWidgetItem* curr_item;
    if(selected_items.size() == 1){
        curr_item = selected_items.front();
        if(curr_item->parent() == NULL){
            int index = ui->csg_treeWidget->currentIndex().row();
            CSGNode *curr_node = m_nodes_list[index].data();
            m_nodes_list.removeAt(index);
            if(curr_node->m_node_type == PRIMITIVE_NODE){
                m_primitives_list.removeAt(curr_node->m_id);
                //                CSGPrimitive* primitive_node = dynamic_cast<CSGPrimitive*>(curr_node);
                ////                switch(primitive_node->m_type){
                ////                case CUBE:
                ////                    m_cube_count--;
                ////                    break;
                ////                case SPHERE:
                ////                    m_sphere_count--;
                ////                    break;
                ////                case CYLINDER:
                ////                    m_cylinder_count--;
                ////                    break;
                ////                case CONE:
                ////                    m_cone_count--;
                ////                    break;
                ////                }
            }

            m_tree_nodes_map.remove(curr_item);
            delete curr_item;
            update_canvas();
        }
    }
}

void MainWindow::hide_object()
{
    if(m_current_node != NULL){
        m_current_node->m_visible = false;
        QList<QTreeWidgetItem*> selected_item = ui->csg_treeWidget->selectedItems();
        QTreeWidgetItem* curr_item = selected_item.front();
        QString text = curr_item->text(0);
        text.append(" Hidden ");
        curr_item->setText(0,text);
    }
    update_canvas();
}

void MainWindow::show_object()
{
    if(m_current_node != NULL){
        m_current_node->m_visible = true;
        QList<QTreeWidgetItem*> selected_item = ui->csg_treeWidget->selectedItems();
        QTreeWidgetItem* curr_item = selected_item.front();
        QString text = curr_item->text(0);
        text.chop(8);
        curr_item->setText(0,text);
    }
    update_canvas();
}

void MainWindow::context_menu(const QPoint & pos)
{
    QPoint globalPos =  ui->csg_treeWidget->mapToGlobal(pos);	// Map the global position to the userlist
    QList<QTreeWidgetItem*> selected_item = ui->csg_treeWidget->selectedItems();
    QTreeWidgetItem* curr_item;


    if(selected_item.size()==1){
        curr_item = selected_item.front();
        if(curr_item->parent() == NULL){
            QMenu *menu = new QMenu();
            m_current_node = m_tree_nodes_map[curr_item];
            bool visible = m_current_node->m_visible;

            if(visible){
                menu->addAction("Hide",this,SLOT(hide_object()));
            }else{
                menu->addAction("Show",this,SLOT(show_object()));
            }
            menu->addAction("Delete",this,SLOT(remove_node()));
            menu->exec(globalPos);
        }
    }
}
